package com.zudada.servermain.security.Interceptor.Token;


import com.zudada.servermain.POJO.enums.*;
import com.zudada.servermain.Properties.TokenProperties;
import com.zudada.servermain.exception.ServiceException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Configuration
@ComponentScan
public class TokenUtil {
    @Autowired
    private TokenProperties tokenProperties;
    @Autowired
    private RedisTemplate redisTemplate;


    public Token createToken(Map<String, Object> claims, boolean longTerm, UserEnums userEnums) {
        Token token = new Token();
        //访问token
        String accessToken = createToken(claims, tokenProperties.getTokenExpireTime());
//
        //        //存redis(在每次校验jwt时要查查看redisd)
        String key = "login::"+ CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken;
//        redisTemplate.opsForValue().set(key, 1,tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
        String userId = (String) claims.get("id");
        //保证用户唯一登录
        redisTemplate.opsForValue().set("userLoginId" + userId+userEnums, accessToken, tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);


        String refreshToken = "11";

        token.setAccessToken(accessToken);
        token.setRefreshToken(refreshToken);
        return token;
    }

    public Token adminCreateToken(Map<String, Object> claims, boolean longTerm, UserEnums userEnums) {
        Token token = new Token();
        //访问token
        String accessToken = AdmincreateToken(claims, tokenProperties.getTokenExpireTime());
//
        //        //存redis(在每次校验jwt时要查查看redisd)
        String key = "login::"+CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken;
        redisTemplate.opsForValue().set(key, 1,tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
//        //刷新token生成策略：如果是长时间有效的token（用于app），则默认15天有效期刷新token。如果是普通用户登录，则刷新token为普通token2倍数
        Long expireTime = longTerm ? 15 * 24 * 60L : tokenProperties.getTokenExpireTime() * 2;
        String refreshToken = createreflushToken(claims, expireTime);
//
        String key1 = "login::"+CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken;
        redisTemplate.opsForValue().set(key, 1,tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
        token.setAccessToken(accessToken);
        token.setRefreshToken(refreshToken);
        return token;
    }

    /**
     * 刷新token
     *
     * @param oldRefreshToken 刷新token
     * @param userEnums       用户枚举
     * @return token
     */
    public Token  refreshToken(String oldRefreshToken, UserEnums userEnums) {

        Map<String, Object> claims;
        try {
            claims = TokenUtil.reflushparseJWT(oldRefreshToken);
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
            //token 过期 认证失败等
            throw new ServiceException(ResultCode.USER_AUTH_EXPIRED);
        }

        //如果缓存中有刷新token &&
        String key1 = "login::"+CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken;
        Object o = redisTemplate.opsForValue().get(key1);
        if (o!=null) {
            Token token = new Token();
            //访问token
            String accessToken = createToken(claims, tokenProperties.getTokenExpireTime());
            String key = "login::"+CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken;
            redisTemplate.opsForValue().set(key, 1,tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
            //如果是信任登录设备，则刷新token长度继续延长
            Long expirationTime = tokenProperties.getTokenExpireTime() * 2;
//            if (true) {
//                expirationTime = 60 * 24 * 15L;
//            }
            //刷新token生成策略：如果是长时间有效的token（用于app），则默认15天有效期刷新token。如果是普通用户登录，则刷新token为普通token2倍数
            String refreshToken = createreflushToken(claims, expirationTime);
            String refreshTokenkey = "login::"+CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken;
            redisTemplate.opsForValue().set(refreshTokenkey, 1,expirationTime, TimeUnit.MINUTES);
            token.setAccessToken(accessToken);
            token.setRefreshToken(refreshToken);
            redisTemplate.delete("login::"+CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken);
//            cache.remove(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken);
            return token;
        } else {
            throw new ServiceException(ResultCode.USER_AUTH_EXPIRED);
        }

    }


    private String createToken(Map<String, Object> claims, Long expirationTime) {
        //JWT 生成
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SecretKeyUtil.generalKey())
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     *
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt) {
        Claims claims = Jwts.parser()
                .setSigningKey(SecretKeyUtil.generalKey())
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }

    private String  AdmincreateToken(Map<String, Object> claims, Long expirationTime) {
        //JWT 生成
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SecretKeyUtil.AdminKey())
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     *
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims AdminparseJWT(String jwt) {
        Claims claims = Jwts.parser()
                .setSigningKey(SecretKeyUtil.AdminKey())
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }

    private String createreflushToken(Map<String, Object> claims, Long expirationTime) {
        //JWT 生成
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SecretKeyUtil.reflushgeneralKey())
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     *
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims reflushparseJWT(String jwt) {
        Claims claims = Jwts.parser()
                .setSigningKey(SecretKeyUtil.reflushgeneralKey())
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}
